﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using BluePortal.Encoding.Core.VideoInfo;
using BluePortal.Framework;
using BluePortal.Medias.Core;
using BluePortal.Encoding.Core.Providers;
using BluePortal.Encoding.Core.ExpressionEncoder;
using BluePortal.Encoding.Core.Interface;
using System.ServiceModel;

namespace BluePortal.Encoding.Core
{
    [ServiceBehaviorAttribute(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]
    public class VideoEncoderProcess : IVideoEncoderProcess
    {
        private BCVideo srvVideo = new BCVideo();

        private MediaStatus finalVideoStatus;
        private string videoFileName;
        private string providerOptions;
        private string provider;
        private string localVideoDir;
        private Guid id;

        public Video VideoBase { get; private set; }
        public string EncodedDirectoryRoot { get; set; }
        public string EncodedFileName { get; private set; }
        public string EncodedDirectory { get; private set; }
        public bool DeleteFile { get; set; }
        public bool DeleteEncodedFile { get; set; }


        public VideoEncoderProcess()
        {
            DeleteFile = true;
            DeleteEncodedFile = false;
            EncodedDirectoryRoot = BluePortal.Streaming.Services.Properties.Settings.Default.EncodedVideoDirectory;
            try
            {
                finalVideoStatus = (MediaStatus)Enum.Parse(typeof(MediaStatus), BluePortal.Streaming.Services.Properties.Settings.Default.FinalVideoStatusAfterEncoded);
            }
            catch (Exception)
            {
                finalVideoStatus = MediaStatus.WaitingAproval;
            }
        }

        public void Delete(Guid vid, string provider, string providerOptions)
        {
            try
            {
                IVideoUploadService uploadSvc = Factory.Create<IVideoUploadService>(provider);
                uploadSvc.ProviderOptions = providerOptions;
                uploadSvc.Delete(vid);
            }
            catch (Exception ex)
            {
                Log.LogError("VideoProcessService", "Delete", 401, ex);
            }
        }

        public void Encode(Guid id, string provider, string providerOptions)
        {
            localVideoDir = BluePortal.Streaming.Services.Properties.Settings.Default.UploadedVideoDirectory;
            var query = from file in Directory.GetFiles(localVideoDir, id.ToString() + ".*") select file;
            if (query.Count() <= 0)
            {
                Log.LogError("VideoProcessService", "ReadLocalFile", String.Format("File not found, id:{0}", id.ToString()), 101);
                Rollback("File not found");
            }

            Encode(id, query.First(), provider, providerOptions);
        }

        public void Encode(Guid id, string filename, string provider, string providerOptions)
        {
            this.provider = provider;
            this.providerOptions = providerOptions;
            this.id = id;
            try
            {
                this.videoFileName = filename;
                VideoBase = srvVideo.FindVideoByID(id);
                if (VideoBase == null)
                {
                    VideoBase = new Video() { ID = id };
                    srvVideo.Create(VideoBase);
                }
                EncodedDirectory = Path.Combine(EncodedDirectoryRoot, id.ToString());

                // -- Encoding and metadata File
                if (VideoBase.Status == (int)MediaStatus.Creation)
                {
                    DoEncode();
                }

                // -- Updating Metadata
                if (VideoBase.Status == (int)MediaStatus.Converted)
                {
                    DoUpdateMetaData();
                }
                // -- Uploading Streaming Service
                if (VideoBase.Status == (int)MediaStatus.MetadataExtracted)
                {
                    DoUpload();
                }

                if (VideoBase.Status == (int)MediaStatus.Online)
                {
                    if (DeleteFile) File.Delete(this.videoFileName);
                    if (DeleteEncodedFile) Directory.Delete(this.EncodedDirectory);
                }
            }
            catch (Exception ex)
            {
                Log.LogError("VideoProcessService", "UploadAndEncode", ex);
                Rollback("unknow error");
            }
            finally
            {
                try
                {
                    // -- Updating Video database
                    if ((VideoBase != null) && (srvVideo != null))
                    {
                        srvVideo.Update(VideoBase);
                    }
                }
                catch (Exception ex)
                {
                    Log.LogError("VideoProcessService", "DatabaseUpdate", 501, ex);
                    Rollback("Updatate video database error");
                }
            }
        }

        private void DoUpload()
        {
            try
            {
                IVideoUploadService uploadSvc = Factory.Create<IVideoUploadService>(provider);
                uploadSvc.ProviderOptions = providerOptions;
                uploadSvc.Upload(VideoBase, EncodedDirectory);

                VideoBase.Provider = provider;
                VideoBase.Status = (int)finalVideoStatus;
            }
            catch (Exception ex)
            {
                Log.LogError("VideoProcessService", "Uploading", 401, ex);
                Rollback("Upload error");
            }
        }

        private void DoUpdateMetaData()
        {
            try
            {
                UpdateThumbnails();
                // -- extract metadatas
                double framerate;
                Size videosize;
                double length;
                //VideoInformation.GetVideoSizeAndFrameRate(EncodedFileName, out videosize, out framerate, out length);
                //videoBase.Framerate = (int)framerate;
                //VideoBase.Height = (int)videosize.Height;
                //VideoBase.Width = (int)videosize.Width;
                //VideoBase.FileSize = (int)new FileInfo(EncodedFileName).Length;
                //List<object> propValues = VideoInformation.GetProperties(EncodedFileName, "Duration", "Bitrate");
                //VideoBase.Duration = (long)Convert.ToInt64(propValues[0]);

                //Get Size otherFormat
                int i = 1;
                string otherVideo;
                foreach (OtherVideoAttach ova in VideoBase.OtherVideoAttaches)
                {
                    otherVideo = Path.Combine(EncodedDirectory, string.Format("{0}_{1}.wmv", VideoBase.ID, i));
                    //VideoInformation.GetVideoSizeAndFrameRate(otherVideo, out videosize, out framerate, out length);
                    //ova.Framerate = (int)framerate;
                    //ova.Height = (int)videosize.Height;
                    //ova.Width = (int)videosize.Width;
                    //ova.FileSize = (int)new FileInfo(otherVideo).Length;
                    i++;
                }


                VideoBase.Status = (int)MediaStatus.MetadataExtracted;
            }
            catch (Exception ex)
            {
                Log.LogError("VideoProcessService", "UpdateMetadata", 301, ex);
                Rollback("UpdateMetadata error");
            }
        }

        private void DoEncode()
        {
            try
            {
                ExpressionEncoderRuntime encoder = new ExpressionEncoderRuntime();
                encoder.MediaEncoderPath = BluePortal.Streaming.Services.Properties.Settings.Default.MediaEncoderPath;
                EncodedFileName = encoder.Encode(videoFileName, VideoBase, EncodedDirectory);

                VideoBase.Status = (int)MediaStatus.Converted;
            }
            catch (Exception ex)
            {
                Log.LogError("VideoProcessService", "Encode", 201, ex);
                Rollback("Encode error");
            }
        }

        private void Rollback(string error)
        {
            //#if DEBUG
            //            Console.WriteLine(String.Format("{0} - ROLLBACK : {1}", message.vid.ToString(), error));
            //#endif
            //            try
            //            {
            //                message.ErrorMessage = error;
            //                ErrorQueue.Send(message);
            //            }
            //            catch (Exception ex)
            //            {
            //                Log.LogError("AbstractVideoProcessService", "Rollback", ex);
            //            }
        }

        /// <summary>
        /// update database with some video informations
        /// </summary>
        /// <param name="srvVideo"></param>
        /// <param name="dir"></param>
        /// <param name="videoBase"></param>
        private void UpdateThumbnails()
        {
            string thumbnail = Path.Combine(EncodedDirectory, string.Format("{0}_Thumb.jpg", VideoBase.ID));
            if (File.Exists(thumbnail))
                VideoBase.Preview = File.ReadAllBytes(thumbnail);

            //Get Thumbnail otherFormat
            int i = 1;
            foreach (OtherVideoAttach ova in VideoBase.OtherVideoAttaches)
            {
                thumbnail = Path.Combine(EncodedDirectory, string.Format("{0}_{1}_Thumb.jpg", VideoBase.ID, i));
                if (File.Exists(thumbnail))
                    ova.Preview = File.ReadAllBytes(thumbnail);
                i++;
            }
        }
    }
}
